local context={}

minetest.register_on_joinplayer(
	function(player)
		context[player:get_player_name()]={}
	end
)

minetest.register_on_leaveplayer(
	function(player)
		context[player:get_player_name()]=nil
	end
)

local function round(num)
	local dec=(10*num)%10
	if dec>=5 then
		return math.ceil(num)
	else
		return math.floor(num)
	end
end

local function findPath(npc,ps1,ps2)
	return minetest.find_path(ps1,ps2,15,npc.stepheight,npc.fear_height-1,"Dijkstra")
end

local function turnTo(npc,pos)
	local currentPos=npc.object:get_pos();
	local ve={x=pos.x-currentPos.x,y=pos.y-currentPos.y,z=pos.z-currentPos.z}
	local px=math.abs(ve.x)
	local pz=math.abs(ve.z)
	local yaw
	--atan(opposite/adjacent)
	if px<pz then
		yaw=math.atan(px,pz)
		if ve.x<0 then
			if ve.z<0 then
				yaw=math.rad(180)-yaw
			end
		elseif ve.x>0 then
			if ve.z<0 then
				yaw=math.rad(180)+yaw
			elseif ve.z>0 then
				yaw=math.rad(360)-yaw
			end
		end
	elseif px>pz then
		yaw=math.atan(pz,px)
		if ve.x<0 then
			if ve.z<0 then
				yaw=math.rad(90)+yaw
			elseif ve.z>0 then
				yaw=math.rad(90)-yaw
			end
		elseif ve.x>0 then
			if ve.z<0 then
				yaw=math.rad(270)-yaw
			elseif ve.z>0 then
				yaw=math.rad(270)+yaw
			end
		end
	end
	npc:set_yaw(yaw)
end

local function moveTo(npc,pos)
	local ps=npc.object:get_pos()
	ps.x=round(ps.x)
	ps.z=round(ps.z)
	if ps.x==pos.x and ps.z==pos.z then
		return true
	else
		turnTo(npc,pos)
		npc:set_velocity(npc.walk_velocity)
		return false
	end
end

local function followPath(npc,path)
	if path and #path>0 then
		if moveTo(npc,path[1]) then 
			if #path==1 then
				npc.state="stand"
				npc:set_animation("stand")
				npc.object:set_velocity({x=0,y=0,z=0})
				npc.h2=npc.h1
				npc.h1=path[1]
			end
			table.remove(path,1)
		end
	end
end

local function spiral(cursor,r,fu)
	cursor.x=round(cursor.x)
	cursor.y=round(cursor.y)
	cursor.z=round(cursor.z)
	fu(cursor)
	cursor.z=cursor.z+1
	fu(cursor)
	for j=1,2*r+1,2 do
		for i=0,j-1,1 do
			cursor.x=cursor.x+1
			fu(cursor)
		end
		for i=0,j,1 do
			cursor.z=cursor.z-1
			fu(cursor)
		end
		for i=0,j,1 do
			cursor.x=cursor.x-1
			fu(cursor)
		end
		for i=0,j+1,1 do
			if i+1<=2*r+2
			then
				cursor.z=cursor.z+1
				fu(cursor)
			end
		end
	end
end

local function findPatrolPoint(npc,pos)
	local nodes={}
	local function add(ps)
		if minetest.get_node(ps).name=="mobs_dungeon:patrol_point" then
			nodes[#nodes+1]={x=ps.x,y=ps.y,z=ps.z}
		end
	end
	spiral({x=pos.x,y=pos.y-1,z=pos.z},5,add)
	if nodes then
		for fi=1,#nodes,1 do
			if not npc.h2 or (not (nodes[fi].x==npc.h2.x 
				and nodes[fi].y==npc.h2.y and nodes[fi].z==npc.h2.z)
				and not (nodes[fi].x==npc.h1.x 
				and nodes[fi].y==npc.h1.y and nodes[fi].z==npc.h1.z))
			then
				return nodes[fi]
			end
		end
	end
	return nil
end

mobs:register_mob(
	"mobs_dungeon:npc", 
	{
		type="npc",
		hp_min=50,
		hp_max=100,
		armor=50,
		passive=false,
		walk_velocity=2,
		run_velocity=3,
		stand_chance=0,
		walk_chance=0,
		jump=false,
		stay_near={},
		pushable=false,
		view_range=15,
		damage=4,
		fear_height=3,
		suffocation=true,
		follow={},
		attacks_monsters=true,
		attack_npcs=false,
		attack_type="dogfight",
		runaway_from={},
		pathfinding=true,
		makes_footstep_sound=true,
		drops={},
		sounds={
			random="mobs_dungeon_npc_random",
			death="mobs_dungeon_npc_death",
			damage="mobs_dungeon_npc_damage"
		},
		visual="mesh",
		collisionbox={-0.35,-1.0,-0.35, 0.35,0.8,0.35},
		textures={{"character.png"}},
		child_texture={{"character.png"}},
		mesh="mobs_character.b3d",
		animation={
			speed_normal=30,
			speed_run=30,
			stand_start=0,
			stand_end=79,
			walk_start=168,
			walk_end=187,
			run_start=168,
			run_end=187,
			punch_start=200,
			punch_end=219,
		},
		owner="",
		order="patrol",

		on_spawn=function(self)
			self.object:set_properties(
				{
					textures={
						"character.png^mobs_dungeon_npc_overlay_"
							..Random:next(1,9)..".png"
					}
				}
			)
		end,

		on_rightclick=function(self, clicker)
			local item=clicker:get_wielded_item()
			local name=clicker:get_player_name()
			local form={
				"size[2,2]",
				"real_coordinates[true]",
				"position[0,0]",
				"anchor[0,0]",
			}
			if self.owner and self.owner == name then
				if self.order == "follow" then
					form[#form+1]="dropdown[0,0;1;order;follow,stand,patrol;1]"
				elseif self.order == "stand" then
					form[#form+1]="dropdown[0,0;1;order;follow,stand,patrol;2]"
				else
					form[#form+1]="dropdown[0,0;1;order;follow,stand,patrol;3]"
				end
			end
			context[name].npc=self
			minetest.show_formspec(name,"mobs_dungeon:menu",table.concat(form,""))
		end,

		do_custom=function(self,dtime)
			if self.state=="attack" or self.state=="runaway" then
				self.mdPath={}
				return
			end
			if self.mdPath and #self.mdPath>0 then
				followPath(self,self.mdPath)
			elseif self.order=="patrol" then
				local target=findPatrolPoint(self,self.object:get_pos())
				if target then
					local path=findPath(self,self.object:get_pos(),target)
					if path then
						self.mdPath=path
						self.state=""
						self:set_animation("walk")
						followPath(self,path)
					end
				end
			end
		end
	}
)

local orders={
	follow=function(npc)
		npc.order="follow"
		npc.state="walk"
		npc.mdPath=nil
	end,
	stand=function(npc)
		npc.order="stand"
		npc.state="stand"
		npc.mdPath=nil
		npc:set_animation("stand")
		npc:set_velocity({x=0,y=0,z=0})
	end,
	patrol=function(npc)
		npc.order="patrol"
	end
}

minetest.register_on_player_receive_fields(
	function(player,formname,fields)
		if formname~="mobs_dungeon:menu" then
			return
		end
		if fields.order then
			local npc=context[player:get_player_name()].npc
			npc.order=fields.order
			orders[fields.order](npc)
		end
	end
)

mobs:spawn(
	{
		name="mobs_dungeon:npc",
		nodes={"mobs_dungeon:patrol_point"},
		min_light=8,
		chance=5000,
		active_object_count=1,
		max_height=0,
	}
)

mobs:register_egg("mobs_dungeon:npc", "NPC", "mobs_dungeon_npc_inventory.png", 0)
